package org.xbdframework.boot.autoconfigure.transaction;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.MethodMapTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor;
import org.springframework.transaction.interceptor.TransactionInterceptor;

/**
 * 以{@link MethodMapTransactionAttributeSource}方式配置的特殊用途的自动化事务配置
 * <p>
 * 此事务配置主要用于统一的事务不满足要求时的情况，如交易记录留痕、操作留痕等。此类情况不管主事务操作如何，本事务操作务必提交。
 * <p>
 * 需要注意{@code PropagationBehavior}对于主事务及当前事务的影响，小心选择。
 *
 * @author luas
 * @since 1.5.0
 */
@Configuration
@AutoConfigureAfter(TransactionAutoConfiguration.class)
@ConditionalOnBean(TransactionMethodMapCustomizer.class)
public class MethodMapTransactionAutoConfiguration {

	@Configuration
	static class MethodMapTransactionConfiguration {

		private final PlatformTransactionManager transactionManager;

		private final List<TransactionMethodMapCustomizer> methodMapTransactionCustomizers;

		MethodMapTransactionConfiguration(PlatformTransactionManager transactionManager,
				ObjectProvider<List<TransactionMethodMapCustomizer>> methodMapTransactionCustomizersProvider) {
			this.transactionManager = transactionManager;
			this.methodMapTransactionCustomizers = Optional
					.ofNullable(methodMapTransactionCustomizersProvider.getIfAvailable())
					.orElse(Collections.emptyList());
		}

		@Bean
		public TransactionInterceptor methodMapTransactionInterceptor() {
			TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
			transactionInterceptor.setTransactionManager(transactionManager);

			customize(transactionInterceptor);

			return transactionInterceptor;
		}

		@Bean
		public TransactionAttributeSourceAdvisor transactionAttributeSourceAdvisor() {
			return new TransactionAttributeSourceAdvisor(
					methodMapTransactionInterceptor());
		}

		private void customize(TransactionInterceptor transactionInterceptor) {

			List<MethodMapTransactionAttributeSource> methodMapTransactionAttributeSources = methodMapTransactionCustomizers
					.stream()
					.map(methodMapTransactionCustomizer -> methodMapTransactionCustomizer
							.customize())
					.collect(Collectors.toList());

			transactionInterceptor
					.setTransactionAttributeSources(methodMapTransactionAttributeSources
							.toArray(new MethodMapTransactionAttributeSource[] {}));
		}

	}

}
